iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
1
Modern Web

技術在走,Vue.js 要有系列 第 25

|D25| 從原始碼看 Vue 擴展 (4) - slot

  • 分享至 

  • xImage
  •  

vue 的 slot 可以讓元件更靈活。

以便編譯順序來說,會先編譯父元件,然後才是子元件

// src/compiler/parser/index.js

function processSlot (el) {
  if (el.tag === 'slot') {
    el.slotName = getBindingAttr(el, 'name')
    if (process.env.NODE_ENV !== 'production' && el.key) {
      warn(
        `\`key\` does not work on <slot> because slots are abstract outlets ` +
        `and can possibly expand into multiple elements. ` +
        `Use the key on a wrapping element instead.`
      )
    }
  } else {
    let slotScope
    if (el.tag === 'template') {
      slotScope = getAndRemoveAttr(el, 'scope')
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && slotScope) {
        warn(
          `the "scope" attribute for scoped slots have been deprecated and ` +
          `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
          `can also be used on plain elements in addition to <template> to ` +
          `denote scoped slots.`,
          true
        )
      }
      el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
    } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
        warn(
          `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +
          `(v-for takes higher priority). Use a wrapper <template> for the ` +
          `scoped slot to make it clearer.`,
          true
        )
      }
      el.slotScope = slotScope
    }
    const slotTarget = getBindingAttr(el, 'slot')
    if (slotTarget) {
      el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
      // preserve slot as an attribute for native shadow DOM compat
      // only for non-scoped slots.
      if (el.tag !== 'template' && !el.slotScope) {
        addAttr(el, 'slot', slotTarget)
      }
    }
  }
}

當解析到 slot 屬性,會加上 slotTarget 属性,在 genData 中處理 slotTarget

// src/compiler/codegen/index.js

if (el.slotTarget && !el.slotScope) {
  data += `slot:${el.slotTarget},`
}

以例子來說
有一個自定義元件 app-layout

<div class="container">
  <header>
      <slot name="header"></slot>
  </header>
  <main>
      <slot>默認內容</slot>
  </main>
  <footer>
      <slot name="footer"></slot>
  </footer>
</div>

在父元件使用

<div id="app">
  <app-layout>
      <h1 slot="header">{{title}}</h1>
      <p>{{msg}}</p>
      <p slot="footer">{{desc}}</p>
  </app-layout>
</div>
import AppLayout from "@/components/app-layout.vue";
export default {
  data() {
    return {
      title: '標題',
      msg: '內容',
      desc: '其他'
    }
  },
  components: {
    AppLayout
  }
})

父元件最後生成的 render 程式碼如下

with(this){
  return _c('div',
    [_c(
      'app-layout',
      [_c(
            'h1',
            { attrs:{"slot":"header"}, 
              slot:"header"
            },
            [ _v( _s(title) ) ]
          ),
      _c(
            'p',
            [ _v( _s(msg) ) ]
        ),
      _c(
            'p',
            { attrs:{"slot":"footer"},
              slot:"footer"
            },
            [ _v( _s(desc) ) ]
        )
      ])
    ], 
    1
  )
}

上一篇
|D24| 從原始碼看 Vue 擴展 (3) - v-model
下一篇
|D26| 自定義元件的資料綁定與`$event`
系列文
技術在走,Vue.js 要有30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言